package com.sns.connect.ui;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.sns.connect.ui.ContactsListAdapter.ContactInfo;
import com.sns.connect.R;

import android.app.Activity;
import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.ContactsContract;
import android.text.Editable;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextWatcher;
import android.text.style.UnderlineSpan;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnFocusChangeListener;
import android.view.View.OnKeyListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;

public class ContactsListMultiple extends Activity implements OnKeyListener {

	private final int UPDATE_LIST_ALL = 1;
	private final int UPDATE_LIST_SEARCH = 2;
	private final int UPDATE_EDIT_TEXT = 3;
	private boolean isAllContactList;
	private static final String ID = "id";
	private static final String NAME = "name";
	private static final String PHONE = "phone";
	private static final String PHOTO = "photo";
	private static final String INPUT = "input";
	private static final String SORTKEY = "sort_key";
	private static final String TAG_NO_SORT = "no_sort";
	ArrayList<HashMap<String, Object>> mContactsList;
	ArrayList<HashMap<String, Object>> mSelectList;
	ArrayList<HashMap<String, Object>> mSearchList;
	private ListView mListView;
	private ImageView mSelectImageView;
	private EditText mSelectName;
	private ContactsListAdapter mAdapter;
	private AsyncQueryHandler asyncQuery;
	private InputMethodManager mInputMethodManager;

	Thread mGetContacts;
	Handler updateListHandler = new Handler() {
		public void handleMessage(Message msg) {
			switch (msg.what) {

			case UPDATE_LIST_ALL:
				isAllContactList = true;
				updateAllList();
				updateSelectFromAll();
				break;
			case UPDATE_LIST_SEARCH:
				isAllContactList = false;
				updateSearchList();
				updateSelectFromSearch();
				break;
			case UPDATE_EDIT_TEXT:
				updateText();
				break;
			}
		}
	};

	TextWatcher mTextWatcher = new TextWatcher() {
		@Override
		public void afterTextChanged(Editable s) {
		}

		@Override
		public void beforeTextChanged(CharSequence s, int start, int count,
				int after) {
		}

		@Override
		public void onTextChanged(CharSequence s, int start, int before,
				int count) {
			Log.w("onTextChanged", s.toString() + "=>" + "start:" + start
					+ ",before:" + before + ",count:" + count);
			if (count == 1
					&& (s.charAt(start) == ',' || s.charAt(start) == '.')) {
				Editable mEditable = mSelectName.getText();
				String mEditText = mEditable.toString();

				int end = start;
				if (end == 0) {
					return;
				}
				int begin = getStartSelectionPos(mEditText, end);
				String name = mEditable.subSequence(begin + 1, end).toString();
				inputContact(name);
				return;
			}
			if (count == 1 && isNumeric("" + s.charAt(start))) {
				int mid = mSelectName.getSelectionStart();
				int begin = getStartSelectionPos(s.toString(), mid);
				int end = getEndSelectionPos(s.toString(), mid);
				if (begin > end || end == 0)
					end = mSelectName.getSelectionStart();
				String name = s.subSequence(begin + 1, end).toString();
				StringBuffer sb = new StringBuffer(name);
				sb.deleteCharAt(mid - begin - 2);
				for (int index = 0; index < mSelectList.size(); index++) {
					String mSelectName = (String) mSelectList.get(index).get(
							NAME);
					int isInput = (Integer) mSelectList.get(index).get(INPUT);
					if (sb != null && sb.toString().equals(mSelectName)
							&& isInput != 0) {
						Log.i("AddName", name);
						HashMap<String, Object> map = new HashMap<String, Object>();
						map.put(NAME, name);
						map.put(PHONE, name);
						map.put(ID, -1);
						map.put(INPUT, 1);
						mSelectList.set(index, map);
						break;
					}
				}
			}
			if (count == 1 && !isNumeric("" + s.charAt(start))) {
				mSearchList.clear();
				int end = mSelectName.getSelectionStart();
				int begin = getStartSelectionPos(s.toString(), end);
				String name = s.subSequence(begin + 1, end).toString();

				searchContact(name);
			}
			if (count > 1) {
				int end = mSelectName.getSelectionStart();
				int begin = getStartSelectionPos(s.toString(), end);
				String name = s.subSequence(begin + 1, end).toString();
				Log.e("-TextChange-name-",name);
				if (!name.contains(",")) {
					mSearchList.clear();
					searchContact(name);
				}
			}
		}
	};

	@SuppressWarnings("unchecked")
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.contacts_list);
		mInputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
		
		mContactsList = new ArrayList<HashMap<String, Object>>();
		mSelectList = new ArrayList<HashMap<String, Object>>();
		mSearchList = new ArrayList<HashMap<String, Object>>();

		isAllContactList = true;

		Intent intent = getIntent();
		Bundle b = intent.getExtras();
		if (b != null) {
			mSelectList = (ArrayList<HashMap<String, Object>>) b
					.getSerializable("HAS_CONTACT_SELECT");
		}

		asyncQuery = new ContactAsyncQueryHandler(getContentResolver());

		mSelectName = (EditText) findViewById(R.id.contact_select_name);
		mSelectName.addTextChangedListener(mTextWatcher);
		mSelectName.setOnKeyListener(this);
		mSelectName.setOnFocusChangeListener(new OnFocusChangeListener() {

			@Override
			public void onFocusChange(View v, boolean hasFocus) {
				Log.e("Focus", "Focus");
				if (v.isFocused()) {
					mInputMethodManager.showSoftInput(v, 0);
				} else {
					mInputMethodManager.hideSoftInputFromWindow(
							v.getWindowToken(), 0);
				}
			}
		});
		
		mListView = (ListView) findViewById(R.id.contact_listview);
		mListView.setOnItemClickListener(new OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				mListView.requestFocus();
				mSelectImageView = (ImageView) view
						.findViewById(R.id.contact_list_select);

				if (mSelectImageView.isShown()) {
					removeContact(position, null);
					return;
				} else {
					if (!mSelectList.contains(mContactsList.get(position))) {
						selectContact(position);
						return;
					}
				}
			}
		});
	}

	@Override
	protected void onResume() {
		super.onResume();
		mListView.requestFocus();
		if (isAllContactList) {
			Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
			String[] projection = { "_id", "display_name", "data1", "sort_key",
					"photo_id" };
			asyncQuery.startQuery(0, null, uri, projection, null, null,
					"sort_key COLLATE LOCALIZED asc");
		}
	}

	@Override
	public boolean onKey(View v, int keyCode, KeyEvent event) {
		if (keyCode == KeyEvent.KEYCODE_DEL
				&& event.getAction() == KeyEvent.ACTION_DOWN) {

			int selection = mSelectName.getSelectionStart();
			Editable mEditable = mSelectName.getText();
			String mEditText = mEditable.toString();
			if (selection == 0)
				return false;

			if (mEditText.charAt(selection - 1) == ','
					|| mEditText.charAt(selection - 1) == '.') {
				int end = mSelectName.getSelectionStart();
				int start = getStartSelectionPos(mEditText, end);

				String name = mEditable.subSequence(start + 1, end - 1)
						.toString();
				int position = 0;
				int isInput = 0;
				int id = 0;
				for (int index = 0; index < mSelectList.size(); index++) {
					String mSelectName = (String) mSelectList.get(index).get(
							NAME);
					if (name != null && name.equals(mSelectName)) {
						id = (Integer) mSelectList.get(index).get(ID);
						isInput = (Integer) mSelectList.get(index).get(INPUT);
						if (isAllContactList) {
							for (int i = 0; i < mContactsList.size(); i++) {
								if (id == (Integer) mContactsList.get(i)
										.get(ID)) {
									position = i;
									break;
								}
							}
						} else {
							for (int i = 0; i < mSearchList.size(); i++) {
								if (id == (Integer) mSearchList.get(i).get(ID)) {
									position = i;
									break;
								}
							}
						}

						if (isInput == 0) {
							mEditable.replace(start + 1, end - 1, "");
							removeContact(position, mSelectName);
						} else if (isInput != 0) {
							mSelectList.remove(index);
						}
						break;
					}
				}
			} else {
				int mid = mSelectName.getSelectionStart();
				int start = getStartSelectionPos(mEditText, mid);
				int end = getEndSelectionPos(mEditText, mid);
				if (start > end || end == 0)
					end = mSelectName.getSelectionStart();
				String name = mEditable.subSequence(start + 1, end).toString();
				Log.e("-deletekey-name",name);

				int position = 0;
				int isInput = 0;
				int id = 0;
				for (int index = 0; index < mSelectList.size(); index++) {
					String mSelectName = (String) mSelectList.get(index).get(
							NAME);
					if (name != null && name.equals(mSelectName)) {
						id = (Integer) mSelectList.get(index).get(ID);
						isInput = (Integer) mSelectList.get(index).get(INPUT);
						if (isAllContactList) {
							for (int i = 0; i < mContactsList.size(); i++) {
								if (id == (Integer) mContactsList.get(i)
										.get(ID)) {
									position = i;
									break;
								}
							}
						} else {
							for (int i = 0; i < mSearchList.size(); i++) {
								if (id == (Integer) mSearchList.get(i).get(ID)) {
									position = i;
									break;
								}
							}
						}
						if (isInput == 0) {
							mEditable.replace(start + 1, end, "");
							removeContact(position, mSelectName);
						} else if (isInput != 0) {
							StringBuffer sb = new StringBuffer(name);
							sb.deleteCharAt(mid - start - 2);
							name = sb.toString();
							HashMap<String, Object> map = new HashMap<String, Object>();
							map.put(NAME, name);
							map.put(PHONE, name);
							map.put(ID, -1);
							map.put(INPUT, 1);
							mSelectList.set(index, map);
						}
						break;
					}
				}
				//TODO 
				String searchName = mEditable.subSequence(start + 1, end-1).toString();
				if (searchName.equals("")) {
					Message msg = new Message();
					msg.what = UPDATE_LIST_ALL;
					updateListHandler.sendMessage(msg);
				} else if (!isNumeric(searchName)) {
					mSearchList.clear();
					searchContact(searchName);
				}
			}
		}
		return false;
	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (keyCode == KeyEvent.KEYCODE_BACK) {
			Intent i = new Intent();
			Bundle b = new Bundle();
			b.putSerializable("GET_CONTACT_SELECT", mSelectList);
			i.putExtras(b);
			setResult(RESULT_OK, i);
			ContactsListMultiple.this.finish();
			return true;
		} else
			return super.onKeyDown(keyCode, event);
	}

	/**
	 * the functions about ListView data operations;
	 * Include:
	 * 			updateAllList():	
	 * 				update the list that contains all the contacts;
	 * 			updateSelectFromAll():	
	 * 				initialize the selected items in the list which contains all the contacts;
	 * 			updateSearchList():	
	 * 				update the list that contains the searched contacts;
	 * 			updateSelectFromSearch:	
	 * 				initialize the selected items in the list which contains the searched contacts;
	 * 			updateText(): 
	 * 				update the EditText with the selected contacts and phone numbers inputed by users;
	 * 			selectContact(int position): 
	 * 				select the contacts and put them into "selected list";
	 * 			inputContact():
	 * 				put the phone numbers inputed by users into "selected list"
	 * 			removeContact():
	 * 				remove selected contacts from list;
	 * 			
	 */
	
	private void updateAllList() {
		List<ContactInfo> items = getListItems();
		mAdapter = new ContactsListAdapter(this, items);
		mListView.setAdapter(mAdapter);

	}

	private void updateSelectFromAll() {
		long size = mSelectList.size();
		if (size == 0)
			return;
		for (int index = 0; index < size; index++) {
			int id = -1;
			id = (Integer) mSelectList.get(index).get(ID);
			if (id >= 0) {
				for (int i = 0; i < mContactsList.size(); i++) {
					if (id == (Integer) mContactsList.get(i).get(ID)) {
						int position = i;
						ContactInfo info = (ContactInfo) mAdapter
								.getItem(position);
						info.name = (String) mContactsList.get(position).get(
								NAME);
						info.phone = (String) mContactsList.get(position).get(
								PHONE);
						info.photo = (String) mContactsList.get(position).get(
								PHOTO);
						info.selected = true;
						mAdapter.notifyDataSetChanged();
						break;
					}
				}
			}
		}
		updateText();
	}

	private void updateSearchList() {
		List<ContactInfo> infos = new ArrayList<ContactInfo>();
		for (int index = 0; index < mSearchList.size(); index++) {
			ContactInfo info = new ContactInfo();
			info.name = (String) mSearchList.get(index).get(NAME);
			info.phone = (String) mSearchList.get(index).get(PHONE);
			info.photo = (String) mSearchList.get(index).get(PHOTO);
			info.sort_key = (String) mSearchList.get(index).get(SORTKEY);
			info.selected = false;
			info.position = index;
			infos.add(info);
		}
		mAdapter.setListItems(infos);
		mListView.setAdapter(mAdapter);
	}

	private void updateSelectFromSearch() {
		long size = mSelectList.size();
		if (size == 0)
			return;
		for (int index = 0; index < size; index++) {
			int id = -1;
			id = (Integer) mSelectList.get(index).get(ID);
			if (id >= 0) {
				for (int i = 0; i < mSearchList.size(); i++) {
					if (id == (Integer) mSearchList.get(i).get(ID)) {
						int position = i;
						ContactInfo info = (ContactInfo) mAdapter
								.getItem(position);
						info.name = (String) mSearchList.get(position)
								.get(NAME);
						info.phone = (String) mSearchList.get(position).get(
								PHONE);
						info.photo = (String) mSearchList.get(position).get(
								PHOTO);
						info.selected = true;
						mAdapter.notifyDataSetChanged();
						break;
					}
				}
			}
		}
	}

	private void updateText() {
		String name = "";
		mSelectName.setText("");
		for (int i = 0; i < mSelectList.size(); i++) {
			name = (String) mSelectList.get(i).get(NAME);
			int isInput = (Integer) mSelectList.get(i).get(INPUT);
			SpannableString spanText = new SpannableString(name + ",");
			if (isInput == 0)
				spanText.setSpan(new UnderlineSpan(), 0, name.length(),
						Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
			mSelectName.append(spanText);
		}
		mListView.requestFocus();
	}

	private void selectContact(int position) {
		if (isAllContactList) {
			String name = (String) mContactsList.get(position).get(NAME);
			String phone = (String) mContactsList.get(position).get(PHONE);
			int id = (Integer) mContactsList.get(position).get(ID);

			HashMap<String, Object> map = new HashMap<String, Object>();
			map.put(NAME, name);
			map.put(PHONE, phone);
			map.put(ID, id);
			map.put(INPUT, 0);
			mSelectList.add(map);

			ContactInfo info = (ContactInfo) mAdapter.getItem(position);
			info.name = (String) mContactsList.get(position).get(NAME);
			info.phone = (String) mContactsList.get(position).get(PHONE);
			info.photo = (String) mContactsList.get(position).get(PHOTO);
			info.selected = true;
			mAdapter.notifyDataSetChanged();
		} else {
			String name = (String) mSearchList.get(position).get(NAME);
			String phone = (String) mSearchList.get(position).get(PHONE);
			int id = (Integer) mSearchList.get(position).get(ID);

			HashMap<String, Object> map = new HashMap<String, Object>();
			map.put(NAME, name);
			map.put(PHONE, phone);
			map.put(ID, id);
			map.put(INPUT, 0);
			mSelectList.add(map);

			ContactInfo info = (ContactInfo) mAdapter.getItem(position);
			info.name = (String) mSearchList.get(position).get(NAME);
			info.phone = (String) mSearchList.get(position).get(PHONE);
			info.photo = (String) mSearchList.get(position).get(PHOTO);
			info.selected = true;
			mAdapter.notifyDataSetChanged();
			
			Message msg = new Message();
			msg.what = UPDATE_LIST_ALL;
			updateListHandler.sendMessage(msg);
		}

		Message msg1 = new Message();
		msg1.what = UPDATE_EDIT_TEXT;
		updateListHandler.sendMessage(msg1);
	}

	private void inputContact(String phone) {
		if (isNumeric(phone)) {
			HashMap<String, Object> map = new HashMap<String, Object>();
			map.put(NAME, phone);
			map.put(PHONE, phone);
			map.put(ID, -1);
			map.put(INPUT, 1);
			if (!mSelectList.contains(map))
				mSelectList.add(map);
		}

		Message msg1 = new Message();
		msg1.what = UPDATE_EDIT_TEXT;
		updateListHandler.sendMessage(msg1);
	}

	private void searchContact(String name) {
		Uri uri = Uri.withAppendedPath(
				ContactsContract.CommonDataKinds.Phone.CONTENT_FILTER_URI,
				Uri.encode(name));
		String[] projection = { "_id", "display_name", "data1", "photo_id" };
		Cursor cursor = getContentResolver().query(uri, projection, null, null,
				"sort_key COLLATE LOCALIZED asc");
		while (cursor.moveToNext()) {
			int idFieldColumnIndex = cursor.getColumnIndex("_id");
			int idField = Integer
					.parseInt(cursor.getString(idFieldColumnIndex));
			int nameFieldColumnIndex = cursor.getColumnIndex("display_name");
			String nameField = cursor.getString(nameFieldColumnIndex);
			int phoneFieldColumnIndex = cursor.getColumnIndex("data1");
			String phoneField = cursor.getString(phoneFieldColumnIndex);
			int photoFieldColumnIndex = cursor.getColumnIndex("photo_id");
			String photoField = cursor.getString(photoFieldColumnIndex);
			HashMap<String, Object> map = new HashMap<String, Object>();
			map.put(ID, idField);
			map.put(NAME, nameField);
			map.put(PHONE, phoneField);
			map.put(SORTKEY, TAG_NO_SORT);
			map.put(PHOTO, photoField);
			if (!mSearchList.contains(map))
				mSearchList.add(map);
		}
		
		Message msg = new Message();
		msg.what = UPDATE_LIST_SEARCH;
		updateListHandler.sendMessage(msg);
	}
	
	private void removeContact(int position, String num) {
		if (isAllContactList) {
			if (num == null)
				num = (String) mContactsList.get(position).get(NAME);
			for (int i = 0; i < mSelectList.size(); i++) {
				String name = (String) mSelectList.get(i).get(NAME);
				if (num != null && num.equals(name)) {
					mSelectList.remove(i);
				}
			}

			if (position >= mContactsList.size())
				return;

			ContactInfo info = (ContactInfo) mAdapter.getItem(position);
			info.name = (String) mContactsList.get(position).get(NAME);
			info.phone = (String) mContactsList.get(position).get(PHONE);
			info.photo = (String) mContactsList.get(position).get(PHOTO);
			info.selected = false;
			mAdapter.notifyDataSetChanged();
		} else {
			if (num == null)
				num = (String) mSearchList.get(position).get(NAME);
			for (int i = 0; i < mSelectList.size(); i++) {
				String name = (String) mSelectList.get(i).get(NAME);
				if (num != null && num.equals(name)) {
					mSelectList.remove(i);
				}
			}

			if (position >= mSearchList.size())
				return;

			ContactInfo info = (ContactInfo) mAdapter.getItem(position);
			info.name = (String) mSearchList.get(position).get(NAME);
			info.phone = (String) mSearchList.get(position).get(PHONE);
			info.photo = (String) mSearchList.get(position).get(PHOTO);
			info.selected = false;
			mAdapter.notifyDataSetChanged();
		}

		Message msg1 = new Message();
		msg1.what = UPDATE_EDIT_TEXT;
		updateListHandler.sendMessage(msg1);
	}

	/**
	 * utility functions
	 */	
	private int getStartSelectionPos(String mEditText, int mid) {
		int start = -1;
		for (int i = mid - 1; i >= 0; i--) {
			if (mEditText.charAt(i) == ',' && i != mid - 1) {
				start = i;
				break;
			}
		}
		return start;
	}

	private int getEndSelectionPos(String mEditText, int mid) {
		int end = 0;
		for (int i = mid - 1; i < mEditText.length(); i++) {
			if (mEditText.charAt(i) == ',' && i != mid - 1) {
				end = i;
				break;
			}
		}
		return end;
	}

	public boolean isNumeric(String str) {
		Pattern pattern = Pattern.compile("[0-9]*");
		Matcher isNum = pattern.matcher(str);
		if (!isNum.matches()) {
			return false;
		}
		return true;
	}

	public boolean isLetter(String str) {
		Pattern pattern = Pattern.compile("^[A-Za-z]+$");
		Matcher isNum = pattern.matcher(str);
		if (!isNum.matches()) {
			return false;
		}
		return true;
	}

	public boolean isChinese(String str) {
		Pattern pattern = Pattern.compile("^[\u4e00-\u9fa5]+$");
		Matcher isNum = pattern.matcher(str);
		if (!isNum.matches()) {
			return false;
		}
		return true;
	}

	private List<ContactInfo> getListItems() {

		List<ContactInfo> infos = new ArrayList<ContactInfo>();

		for (int index = 0; index < mContactsList.size(); index++) {
			ContactInfo info = new ContactInfo();
			info.sort_key = (String) mContactsList.get(index).get(SORTKEY);
			info.name = (String) mContactsList.get(index).get(NAME);
			info.phone = (String) mContactsList.get(index).get(PHONE);
			info.photo = (String) mContactsList.get(index).get(PHOTO);
			info.selected = false;
			info.position = index;
			infos.add(info);
		}

		return infos;
	}
	
	private class ContactAsyncQueryHandler extends AsyncQueryHandler {

		public ContactAsyncQueryHandler(ContentResolver cr) {
			super(cr);

		}

		@Override
		protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
			if (cursor != null && cursor.getCount() > 0) {
				List<ContentValues> list = new ArrayList<ContentValues>();
				cursor.moveToFirst();
				for (int i = 0; i < cursor.getCount(); i++) {
					cursor.moveToPosition(i);
					HashMap<String, Object> map = new HashMap<String, Object>();
					String id = cursor.getString(0);
					String name = cursor.getString(1);
					String phone = cursor.getString(2);
					String sortKey = cursor.getString(3);
					String photo = cursor.getString(4);
					if (phone.startsWith("+86")) {
						map.put(ID, Integer.parseInt(id));
						map.put(NAME, name);
						map.put(PHONE, phone.substring(3));
						map.put(SORTKEY, sortKey);
						map.put(PHOTO, photo);
					} else {
						map.put(ID, Integer.parseInt(id));
						map.put(NAME, name);
						map.put(PHONE, phone);
						map.put(SORTKEY, sortKey);
						map.put(PHOTO, photo);
					}
					if (!mContactsList.contains(map))
						mContactsList.add(map);
				}
				if (list.size() >= 0) {
					Message msg = new Message();
					msg.what = UPDATE_LIST_ALL;
					updateListHandler.sendMessage(msg);
				}
			}
		}

	}
}